/**
 * @file schedule.c
 * @synopsis  调度otdr通道使用spi进行测量
 * @author wen wjcapple@163.com
 * @version 2.0
 * @date 2016-06-24
 */
#include <semaphore.h>

#include "otdr_ch.h"
#include "hb_app.h"
#include "global.h"
#include "adpt_otdr.h"
#include "adpt_alarm.h"
//spi 读取数据的缓冲区 spi 数据格式： fe 4B 2B fe 4B 2B
#define SPI_OP_SPACE_MS		1000 //spi设备的操作间隔  
_tagSpiDevCtrl spi_ctrl;
struct fpga_dev *fpga_device;
//记录线程信息
struct _tagThreadInfo tsk_measurp_info;
extern struct _tagCHAlarmDis chAlarmDis;

//存放算法曲线的缓冲区
extern struct _tagOtdrTestCtrl otdrTestCtrl;
extern struct _tagParalleCtrl *paralleCtrlBuf;
extern struct _tagUsrOtdrTest *usrOtdrTestBuf_P;
/* --------------------------------------------------------------------------*/
/**
 * @synopsis  tsk_measur_p 并行otdr工作线程
 *
 * @param arg
 *
 * @returns   
 */
/* ----------------------------------------------------------------------------*/
pthread_key_t key_ch;
#if 0
int32_t tsk_measur_p(void *arg)
{
	int32_t ch, ret;
	struct _tagCHPara usr_test_para;
	struct _tagCHPara *pch_para;
	struct _tagOtdrDev *potdr_dev;
	struct _tagCHFiberSec *pch_fiber_sec;
	struct _tagHostOtdrPara HostPara;
	struct _tagAlarmModuleAdpt *pAlarmAdpt;
	FiberAlarmInfo_t CurAlarm;
	struct _tagThreadInfo *pthread_info;
	struct glink_addr addr;
	struct tms_ack ack;
	memset(&ack, 0, sizeof(struct tms_ack));
	pthread_mutex_t *pmutex;
	OtdrTestResult_t ch_tr;
	OTDRCurveInfo_t cur_alarm_curv;
	OTDREvent_t ep_buf[MAX_EVENT_NUM];   //事件缓冲区    
	FiberSectionByEvent_t efs_buf[MAX_EVENT_NUM];     //光线段缓冲区
	int32_t is_switch,count,tmp, pasue_count;
	uint32_t pasue_num, monitor_over;
	int32_t pulse_used,stat_end, cat_pt;	//测试脉宽，测试次数
	struct _tagUsrOtdrTest *pusrOtdrTest;
	struct _tagParalleCtrl *pparalleCtrl;
	float total_loss;
	DeviceInfo_t *pfpga_device;
	sem_t *psem_appoint;

	is_switch = 0;

	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);
	ch = pthread_info->ch;
	assert(ch >=0 && ch < CH_NUM);
	pthread_setspecific(key_ch,&ch);
	printf("%s %s ch %d pid %d  self_id 0x%x \n",
			__FILENAME__, __FUNCTION__,ch, (long int)syscall(224),(int)pthread_self());
	ch_tr.Ai = malloc(sizeof(float)*DATA_LEN);

	if(ch_tr.Ai == NULL)
		exit_self(errno,__FUNCTION__,__LINE__,"malloc ch_ti error");

	//告警模块查找告警的时候使用的曲线
	cur_alarm_curv.testresult.eventinfo = ep_buf;
	cur_alarm_curv.testresult.sectioninfo = efs_buf;
	cur_alarm_curv.testresult.curvepointnum = DATA_LEN - 1000;
	cur_alarm_curv.testresult.curvepoint = ch_tr.Ai;

	count = 0;
	pmutex = &paralleCtrlBuf[ch].mutex_tsk;
	pfpga_device = &paralleCtrlBuf[ch].device;
	pasue_count = 0;
	pasue_num = 0xABA95000; //10us计，8个小时后启动
	//memset(&chAlarmDis, 0, sizeof(struct _tagCHAlarmDis));
	pusrOtdrTest = &usrOtdrTestBuf_P[ch];
	psem_appoint = &paralleCtrlBuf[ch].sem_appoint;
	pparalleCtrl = &paralleCtrlBuf[ch];
	/*
	 * 系统初始化阶段，根据光纤段是否配置，将alarm_syn设置成0，或者1.
	 * 如果光纤段配置，在第一次确认告警产生或者告警消失之后，设置成1
	 * 如果alarm_syn == monitor_over,那么就打开发送告警开关
	 */
	tmp = otdrTestCtrl.ch_num;
	monitor_over = pow(2,tmp) - 1;
	pulse_used = 0;

	while(1){
		pthread_mutex_lock(pmutex);

		pch_fiber_sec = &chFiberSec[ch];
		potdr_dev = &otdrDev[ch];
		is_switch = 0;

		//处理点名测量
		ret = sem_trywait(psem_appoint);
		if(ret == 0){
			if(pusrOtdrTest->state == USR_OTDR_TEST_WAIT){
				pparalleCtrl->stop = 0;
				deal_appoint_test(pusrOtdrTest,&ch_tr,pfpga_device,cb_stop_paralle);
				goto go_on;
			}
		}
		//minishell里面可以暂停轮询监测 如果忘记重新开启，8小时候自动重新监测
		if(otdrTestCtrl.pause_cyc == 1)
		{
			pasue_count++;
			if(pasue_count > pasue_num)
				otdrTestCtrl.pause_cyc = 0;
			goto go_on;
		}
		else
			pasue_count = 0;
		
		ret = sem_trywait(&potdr_dev->sem_msg);
		//更新参数
		if(ret == 0 && potdr_dev->ch_ctrl.refresh_para){
			refresh_ch_monitor_para(pch_fiber_sec, potdr_dev);
			//熄灭告警灯
			set_ch_alarm_status(ch, 0,&chAlarmDis);
			if(potdr_dev->ch_ctrl.is_cfged == 0)
				otdrTestCtrl.alarm_syn = otdrTestCtrl.alarm_syn | (1<<ch);

			//如果被清空了光纤段配置，
			if((potdr_dev->ch_ctrl.is_cfged == 0)&&otdrTestCtrl.is_send_alarm == 0&&\
					(otdrTestCtrl.alarm_syn == monitor_over))
			{
				otdrTestCtrl.is_send_alarm = 1;
				ret_total_curalarm2host();
			}

		}

		//循环测量
		if(potdr_dev->ch_ctrl.is_cfged)
		{

			pch_para = &otdrDev[ch].ch_para;
			//pch_para->PulseWidth_ns = adpt_test_pulse(&otdrDev[ch]);
			/*
			 * 第一次测试，使用标准脉宽，第二次根据测试结果修改脉宽，第三次仍然根据测试结果修改
			 * 如果前两次测试告警相同，那么不再进行第二次测试
			*/
			if(pulse_used == 0)
				pch_para->PulseWidth_ns = otdrDev[ch].ch_ctrl.st_pulse;
			else
				pch_para->PulseWidth_ns = pulse_used;
			pch_para->NonRelectThreshold = 0;

			ret = get_ch_otdr_curv(ch, pch_para,&ch_tr,pfpga_device,cb_stop_paralle);
			assert(ret == 0 || ret == 1);
			if(ret != 0 || pparalleCtrl->stop != 0)
				PR_X("get otdr curv error ret = %d ch = %d\n",ret,ch);
			else
			{
				otdrDev[ch].ch_ctrl.cur_fiber_len = ch_tr.FiberLen;
				otdrDev[ch].ch_ctrl.test_count++;
			}
			if(pparalleCtrl->stop != 0)
				goto go_on;

			get_cyc_curv_from_algor(&otdrDev[ch].curv, &ch_tr, ch + ch_offset);
			convert_curv_aglo2alarmmoudle(&ch_tr,&cur_alarm_curv);
			//防止这段时间光线段配置发生改变
			quick_lock(&pch_fiber_sec->lock);
			
			if(pch_fiber_sec->para.is_initialize){
				
				pAlarmAdpt = &pch_fiber_sec->adpt_moudle;
				//准备查找告警
				CurAlarm.fiberalarm = pAlarmAdpt->cur_alarm.fiberalarm;
				CurAlarm.alarmnum = pAlarmAdpt->cur_alarm.alarmnum;	
				ret = find_fiber_alarm(&pAlarmAdpt->std_curv, 
						&cur_alarm_curv, 
						&pAlarmAdpt->fiber_sec, //光纤段信息
						CurAlarm.alarmnum, 
						&CurAlarm,
						NULL);   //允许的最大告警数目
				assert(ret == 0);

				printf("ch %d pl %d test num %d \n", ch,pch_para->PulseWidth_ns,\
					       	pAlarmAdpt->alarm_cache.test_num);

				refresh_fiber_statistc(&ch_tr, pch_fiber_sec, ch + ch_offset);
				if(ret == 0)
				{
					ret = refresh_alarm_cache(&pAlarmAdpt->alarm_cache,&CurAlarm, &pch_fiber_sec->alarm);
					if(ret == SEND_ALARM_AND_SWITCH_CH)
						memcpy(pch_fiber_sec->alarm.pcurv, &potdr_dev->curv.curv, \
								sizeof(struct _tagUpOtdrCurv));
					//三次测试之内，上次发现告警，为前提；拼接点从有到无或者链长小于B标杆的0.667
					/*
					if(ret == CONTINUE_TEST && CurAlarm.alarmnum > 0 && 
							(pch_fiber_sec->para.otdr_param.rsv[1] > 0 && ch_tr.tp.rsvd2 == 0 || \
							ch_tr.FiberLen < potdr_dev->ch_ctrl.sec_loss.alarm_dis)
					  )*/
					//2017-09-05 如果前面平顶，又没有找到拼接点，就将脉宽
					/*
					stat_end = (ch_tr.tp.rsvd2 >> 16)&0xffff;
					cat_pt = ch_tr.tp.rsvd2&0xffff;
					printf("ch %d stat_end %d cat_pt %d pulse_used %d \n",ch, stat_end, cat_pt, pulse_used);
					if(ret == CONTINUE_TEST && CurAlarm.alarmnum > 0 && stat_end > 0 && cat_pt <= 0&&\
					 ch_tr.FiberLen < potdr_dev->ch_ctrl.sec_loss.alarm_dis && pulse_used == 0 )
					*/
					//2017-09-14 告警级别为1，同时链长变短
					tmp = get_max_serious_alarm(&CurAlarm);
					printf("ch %d alarm_num %d lev %d fiber_len %0.3f 0.67linb %.3f\n",ch,CurAlarm.alarmnum,\
							tmp,ch_tr.FiberLen, potdr_dev->ch_ctrl.sec_loss.alarm_dis);
					if(ret == CONTINUE_TEST && CurAlarm.alarmnum > 0 &&\
						       	pulse_used == 0 && tmp == FIBER_ALARM_LEV1 && \
							ch_tr.FiberLen < potdr_dev->ch_ctrl.sec_loss.alarm_dis )
					{
						pulse_used = get_test_pulse_v1(potdr_dev, &ch_tr);
						printf("ch %d change to pl %d  \n",ch, pulse_used);
					}	
				}
				else 
					ret = SWITCH_CH;
			}
			quick_unlock(&pch_fiber_sec->lock);
			//判断是否切换通道
			if(ret != CONTINUE_TEST)
			{
				is_switch = 1;
				count++;
				otdrTestCtrl.alarm_syn = otdrTestCtrl.alarm_syn | (1<<ch);
			}
			/*
			 * 只有全部通道都监测完成，才允许发送告警。每个通道占据
			 * alarm_syn中的一位
			*/	
			if((otdrTestCtrl.alarm_syn == monitor_over) &&
				       	(( 0 == otdrTestCtrl.is_send_alarm )|| ( ret == SEND_ALARM_AND_SWITCH_CH ))
					)
			{
				if(0 == otdrTestCtrl.is_send_alarm)
					otdrTestCtrl.is_send_alarm = 1;
				ret_total_curalarm2host();
			}
			//如果没有配置，灭灯。如果告警数目小于0也灭灯
			if(pch_fiber_sec->alarm.alarm_num > 0)
					set_ch_alarm_status(ch,1,&chAlarmDis);
			else
					set_ch_alarm_status(ch,0,&chAlarmDis);

		}
		else
		{
			is_switch = 1;
			set_ch_alarm_status(ch, 0, &chAlarmDis);
		}

go_on:
		if(is_switch){
			
			pulse_used = 0;
			is_switch = 0;
			//ch++;		//并行otdr，永不切换
			ch = ch % CH_NUM;
		}
		pthread_mutex_unlock(pmutex);
		//如果用户停止，多睡一会
		if(pparalleCtrl->stop)
			usleep(90000);
		else
			usleep(10);
	}



}
#endif
int32_t tsk_measur_p(void *arg)
{
	int32_t ch, ret;
	struct _tagCHPara usr_test_para;
	struct _tagOtdrDev *potdr_dev;
	struct _tagThreadInfo *pthread_info;
	pthread_mutex_t *pmutex;
	OtdrTestResult_t ch_tr;
	struct _tagUsrOtdrTest *pusrOtdrTest;
	struct _tagParalleCtrl *pparalleCtrl;
	DeviceInfo_t *pfpga_device;
	sem_t *psem_appoint;


	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);
	ch = pthread_info->ch;
	assert(ch >=0 && ch < CH_NUM);
	pthread_setspecific(key_ch,&ch);
	printf("%s %s ch %d pid %d  self_id 0x%x \n",
			__FILENAME__, __FUNCTION__,ch, (long int)syscall(224),(int)pthread_self());
	ch_tr.Ai = malloc(sizeof(float)*DATA_LEN);

	if(ch_tr.Ai == NULL)
		exit_self(errno,__FUNCTION__,__LINE__,"malloc ch_ti error");


	pmutex = &paralleCtrlBuf[ch].mutex_tsk;
	pfpga_device = &paralleCtrlBuf[ch].device;
	pusrOtdrTest = &usrOtdrTestBuf_P[ch];
	psem_appoint = &paralleCtrlBuf[ch].sem_appoint;
	pparalleCtrl = &paralleCtrlBuf[ch];

	while(1)
	{

		potdr_dev = &otdrDev[ch];

		//处理点名测量
		ret = sem_wait(psem_appoint);
		if(ret == 0)
		{
			if(pusrOtdrTest->state == USR_OTDR_TEST_WAIT)
			{
				pparalleCtrl->stop = 0;
				ch = pusrOtdrTest->para.pipe;
				chAlarmDis.ch[ch] = 1;
				deal_appoint_test(pusrOtdrTest,&ch_tr,pfpga_device,cb_stop_paralle);
				chAlarmDis.ch[ch] = 0;
			}
		}
	}


}
